﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using ElectronicCommerce.Framework.Data.Entity.EntityQueryCondition;
using ElectronicCommerce.Framework.Data.Utility.EntityQueryConditionBuilder;

namespace ElectronicCommerce.Framework.Data.Utility.SqlExtension
{
    public static class DataExtension
    {
        public static string ToQueryString<T>(T condition, bool withWhere)
        {
            return ToQueryString<T>(condition, withWhere, "");
        }

        public static string ToQueryString<T>(T condition, bool withWhere, string preStr)
        {
            StringBuilder sb = new StringBuilder();
            foreach (var prop in condition.GetType().GetProperties())
            {
                #region Check

                var value = prop.GetValue(condition, null);
                if (value == null)
                {
                    continue;
                }
                var name = prop.Name;
                if ("PageNumber".Equals(name) || "PageSize".Equals(name))
                {
                    continue;
                }

                #endregion

                Type currentType = prop.PropertyType;

                #region Build range for value type

                //if (prop.PropertyType.IsValueType)
                //{
                //    value = prop.PropertyType.GetGenericTypeDefinition() == typeof(Nullable<>) 
                //        ? 
                //            BuildRange(prop.PropertyType.GetGenericArguments()[0], value)
                //        : 
                //            BuildRange(prop.PropertyType, value);

                //    currentType = value.GetType();
                //}

                #endregion

                #region Build query

                if (currentType == typeof(Range<int, NumberOperator>))
                {
                    var temp = value as Range<int, NumberOperator>;
                    if (temp == null) continue;

                    sb.AppendFormat(preStr + "{0} AND ", QueryBuildHelper.SqlGetConditionStr(temp, name));
                    continue;
                }

                if (currentType == typeof(Range<int?, NumberOperator>))
                {
                    var temp = value as Range<int?, NumberOperator>;

                    sb.AppendFormat(preStr + "{0} AND ", QueryBuildHelper.SqlGetConditionStr(temp, name));
                    continue;
                }

                if (currentType == typeof(Range<string, StringOperator>))
                {
                    var temp = value as Range<string, StringOperator>;
                    if (temp == null) continue;

                    sb.AppendFormat(preStr + "{0} AND ", QueryBuildHelper.SqlGetConditionStr(temp, name));
                    continue;
                }

                if (currentType == typeof(Range<DateTime, DateTimeOperator>))
                {
                    var temp = value as Range<DateTime, DateTimeOperator>;
                    if (temp == null) continue;

                    sb.AppendFormat(preStr + "{0} AND ", QueryBuildHelper.SqlGetConditionStr(temp, name));
                    continue;
                }


                if (currentType == typeof(Range<DateTime?, DateTimeOperator>))
                {
                    var temp = value as Range<DateTime?, DateTimeOperator>;

                    sb.AppendFormat(preStr + "{0} AND ", QueryBuildHelper.SqlGetConditionStr(temp, name));
                    continue;
                }

                if (currentType == typeof(Range<double, NumberOperator>))
                {
                    var temp = value as Range<double, NumberOperator>;
                    if (temp == null) continue;

                    sb.AppendFormat(preStr + "{0} AND ", QueryBuildHelper.SqlGetConditionStr(temp, name));
                    continue;
                }

                if (currentType == typeof(Range<decimal, NumberOperator>))
                {
                    var temp = value as Range<decimal, NumberOperator>;
                    if (temp == null) continue;

                    sb.AppendFormat(preStr + "{0} AND ", QueryBuildHelper.SqlGetConditionStr(temp, name));
                    continue;
                }


                if (currentType == typeof(Range<decimal?, NumberOperator>))
                {
                    var temp = value as Range<decimal?, NumberOperator>;

                    sb.AppendFormat(preStr + "{0} AND ", QueryBuildHelper.SqlGetConditionStr(temp, name));
                    continue;
                }


                if (currentType == typeof(Range<Int64?, NumberOperator>))
                {
                    var temp = value as Range<Int64?, NumberOperator>;

                    sb.AppendFormat(preStr + "{0} AND ", QueryBuildHelper.SqlGetConditionStr(temp, name));
                    continue;
                }


                if (currentType == typeof(Range<Int64, NumberOperator>))
                {
                    var temp = value as Range<Int64, NumberOperator>;
                    if (temp == null) continue;

                    sb.AppendFormat(preStr + "{0} AND ", QueryBuildHelper.SqlGetConditionStr(temp, name));
                    continue;
                }

                Type columnType = prop.PropertyType;
                if (prop.PropertyType.IsGenericType && prop.PropertyType.GetGenericTypeDefinition() == typeof(Range<,>))
                {
                    columnType = prop.PropertyType.GetGenericArguments()[0];

                    if (columnType.IsGenericType && columnType.GetGenericTypeDefinition() == typeof(Nullable<>))
                    {
                        columnType = columnType.GetGenericArguments()[0];
                    }

                    if (columnType.IsEnum)
                    {
                        var temp = new Range<int, NumberOperator>();
                        foreach (var item in value.GetType().GetProperties())
                        {
                            PropertyInfo tempprop = temp.GetType().GetProperty(item.Name);
                            object val = item.GetValue(value, null);
                            if (val != null && (item.Name == "Value" || item.Name == "Start" || item.Name == "End"))
                            {
                                tempprop.SetValue(temp, Convert.ToInt32(val), null);
                            }
                            else if (val != null && item.Name == "ListValue")
                            {
                                var list = val as IList;
                                List<int> returnVal = new List<int>();
                                for (int i = 0; i < list.Count; i++)
                                {
                                    returnVal.Add(Convert.ToInt32(list[i]));
                                }
                                tempprop.SetValue(temp, returnVal, null);
                            }
                            else
                            {
                                tempprop.SetValue(temp, val, null);
                            }
                        }

                        sb.AppendFormat(preStr + "{0} AND ", QueryBuildHelper.SqlGetConditionStr(temp, name));
                        continue;
                    }
                }

                if (prop.Name == "Condition" && (value != null && !string.IsNullOrEmpty(value.ToString())))
                {
                    sb.AppendFormat("({0}) AND ", value);
                }
                #endregion
            }

            #region Filter Query

            if (withWhere)
            {
                if (sb.Length != 0)
                {
                    sb.Insert(0, "WHERE ");
                }
            }

            var rs = sb.ToString();
            if (rs.Contains("AND"))
            {
                rs = rs.Trim().TrimEnd('A', 'N', 'D');
            }

            #endregion



            return rs;
        }

        //       public static string RangeToString(Range<T, N> rangeCondition)
        //       {
        //           rangeCondition.g
        //var value = prop.GetValue(condition, null);
        //               if (value == null)
        //               {
        //                   continue;
        //               }
        //               var name = prop.Name;
        //               if ("PageNumber".Equals(name) || "PageSize".Equals(name))
        //               {
        //                   continue;
        //               }

        //               #endregion

        //               Type currentType = prop.PropertyType;

        //               #region Build query

        //               if (currentType == typeof(Range<int, NumberOperator>))
        //               {
        //                   var temp = value as Range<int, NumberOperator>;
        //                   if (temp == null) continue;

        //                   sb.AppendFormat(preStr + "{0} AND ", QueryBuildHelper.SqlGetConditionStr(temp, name));
        //                   continue;
        //               }

        //               if (currentType == typeof(Range<int?, NumberOperator>))
        //               {
        //                   var temp = value as Range<int?, NumberOperator>;

        //                   sb.AppendFormat(preStr + "{0} AND ", QueryBuildHelper.SqlGetConditionStr(temp, name));
        //                   continue;
        //               }

        //               if (currentType == typeof(Range<string, StringOperator>))
        //               {
        //                   var temp = value as Range<string, StringOperator>;
        //                   if (temp == null) continue;

        //                   sb.AppendFormat(preStr + "{0} AND ", QueryBuildHelper.SqlGetConditionStr(temp, name));
        //                   continue;
        //               }

        //               if (currentType == typeof(Range<DateTime, DateTimeOperator>))
        //               {
        //                   var temp = value as Range<DateTime, DateTimeOperator>;
        //                   if (temp == null) continue;

        //                   sb.AppendFormat(preStr + "{0} AND ", QueryBuildHelper.SqlGetConditionStr(temp, name));
        //                   continue;
        //               }


        //               if (currentType == typeof(Range<DateTime?, DateTimeOperator>))
        //               {
        //                   var temp = value as Range<DateTime?, DateTimeOperator>;

        //                   sb.AppendFormat(preStr + "{0} AND ", QueryBuildHelper.SqlGetConditionStr(temp, name));
        //                   continue;
        //               }

        //               if (currentType == typeof(Range<double, NumberOperator>))
        //               {
        //                   var temp = value as Range<double, NumberOperator>;
        //                   if (temp == null) continue;

        //                   sb.AppendFormat(preStr + "{0} AND ", QueryBuildHelper.SqlGetConditionStr(temp, name));
        //                   continue;
        //               }

        //               if (currentType == typeof(Range<decimal, NumberOperator>))
        //               {
        //                   var temp = value as Range<decimal, NumberOperator>;
        //                   if (temp == null) continue;

        //                   sb.AppendFormat(preStr + "{0} AND ", QueryBuildHelper.SqlGetConditionStr(temp, name));
        //                   continue;
        //               }


        //               if (currentType == typeof(Range<decimal?, NumberOperator>))
        //               {
        //                   var temp = value as Range<decimal?, NumberOperator>;

        //                   sb.AppendFormat(preStr + "{0} AND ", QueryBuildHelper.SqlGetConditionStr(temp, name));
        //                   continue;
        //               }
        //       }

        #region Helper

        private static object BuildRange(Type type, object value)
        {
            #region Build query

            if (type.BaseType != typeof(object))
            {
                type = type.BaseType;
            }

            if (type == typeof(DateTime))
            {
                return new Range<DateTime, DateTimeOperator>()
                {
                    Operator = DateTimeOperator.Equal,
                    Value = Convert.ToDateTime(value)
                };
            }
            else if (type == typeof(Enum))
            {
                var temp = value as Enum;
                return new Range<int, NumberOperator>()
                {
                    Operator = NumberOperator.Equal,
                    Value = Convert.ToInt32(temp)
                };
            }
            else if (type == typeof(int))
            {
                return new Range<int, NumberOperator>()
                {
                    Operator = NumberOperator.Equal,
                    Value = Convert.ToInt32(value)
                };
            }
            else if (type == typeof(decimal))
            {
                return new Range<decimal, NumberOperator>()
                {
                    Operator = NumberOperator.Equal,
                    Value = Convert.ToDecimal(value)
                };
            }
            else if (type == typeof(double))
            {
                return new Range<double, NumberOperator>()
                {
                    Operator = NumberOperator.Equal,
                    Value = Convert.ToDouble(value)
                };
            }
            else
            {
                return new Range<string, StringOperator>()
                {
                    Operator = StringOperator.Equal,
                    Value = Convert.ToString(value)
                };
            }

            #endregion
        }

        #endregion
    }

}
